import Cesium from '../Ces/Cesium'

const WeIsNodata = `
  bool WeIsNodata(float value,float minValue,float filterMin,float filterMax){
    return value - minValue < 1e-6 || value < filterMin || value > filterMax;
  }
`;

const WeGetSubGridUV = `
  vec2 WeGetSubGridUV(vec2 uv,float offsetX,float xSize,float offsetY,float ySize){
    float x = offsetX + uv.x * xSize;
    float y = offsetY + uv.y * ySize;
    return vec2(x,y);
  }
`;

const WeGetImageryLocalUV = `
  vec2 WeGetImageryLonLat(vec2 uv,vec4 localExtent)
  {
    float longitude = localExtent.x + uv.x * (localExtent.z - localExtent.x);
    float latitude = localExtent.y + uv.y * (localExtent.w - localExtent.y);
    return vec2(longitude,latitude);
  }

  vec2 WeGetImageryLocalUV(vec2 lonLat,vec4 extent)
  {
    float west = extent.x;
    float south = extent.y;
    float east = extent.z;
    float north = extent.w;
    float x = (lonLat.x - west) / (east - west);
    float y = (lonLat.y - south) / (north - south);
    x = clamp(x,0.0,1.0);
    y = clamp(y,0.0,1.0);
    return vec2(x,y);
  }
`;

const WeGetRampColor = `
  vec3 WeGetRampColor(vec3 color,sampler2D legend,float value,float minValue,float maxValue){
    float i = (value - minValue) / (maxValue - minValue);
    vec4 colorClamp = texture(legend, vec2(i, 1.0));
    float clampB = colorClamp.x * 255.0 + minValue;
    float clampE = colorClamp.y * 255.0 + minValue;
    float clampV = (value - clampB) / (clampE - clampB);
    clampV = clamp(clampV,0.0,1.0);
    clampV = abs(clampV - 0.5) * 2.0;
    float f = pow(clampV,20.0) * 0.15;
    return mix(color.rgb,vec3(0.0), f);
  }
`;

const WePhong = `
  vec3 WePhong(vec3 diffuse,float specular,vec3 toEye,vec3 lightDirectionEC,vec3 normalEC){
      float diffuseIntensity = czm_getLambertDiffuse(lightDirectionEC, normalEC);
      diffuseIntensity = 0.5 + 0.5 * diffuseIntensity;
      float specularIntensity = czm_getSpecular(lightDirectionEC, toEye, normalEC, 1.0);
      vec3 color = vec3(0.0);
      color += diffuse * diffuseIntensity * czm_lightColor;
      color += specular * specularIntensity * czm_lightColor;
      return color;
  }
`

const WeCartesian3FromRadians = `
vec3 we_cartesian3FromRadians(vec3 pos){
  vec3 radiiSquared = czm_ellipsoidRadii * czm_ellipsoidRadii;
  float stLongitude = pos.x;
  float stLatitude = pos.y;

  float cosLatitude = cos(stLatitude);
  float nX = cosLatitude * cos(stLongitude);
  float nY = cosLatitude * sin(stLongitude);
  float nZ = sin(stLatitude);
  vec3  nR = normalize(vec3(nX,nY,nZ));

  float kX = radiiSquared.x * nX;
  float kY = radiiSquared.y * nY;
  float kZ = radiiSquared.z * nZ;
  float gamma = sqrt(kX * nX + kY * nY + kZ * nZ) + 1e-10;
  vec3 position = vec3(0.0);
  float heightSample = pos.z;
  position.x = kX / gamma + nX * heightSample;
  position.y = kY / gamma + nY * heightSample;
  position.z = kZ / gamma + nZ * heightSample;

  return position;
}

mat4 we_eastNorthUpToFixedFrame(vec3 position) {
  vec3 ecef = we_cartesian3FromRadians(position);

  float lon = position.x;
  float lat = position.y;

  float sinLat = sin(lat);
  float cosLat = cos(lat);
  float sinLon = sin(lon);
  float cosLon = cos(lon);

  vec3 east = vec3(-sinLon, cosLon, 0.0);
  vec3 north = vec3(-sinLat * cosLon, -sinLat * sinLon, cosLat);
  vec3 up = vec3(cosLat * cosLon, cosLat * sinLon, sinLat);
  mat4 transform = mat4(
      vec4(east, 0.0),
      vec4(north, 0.0),
      vec4(up, 0.0),
      vec4(ecef, 1.0)
  );
  return transform;
}


mat3 we_mat3FromRotationX(float angle) {
  float c = cos(angle);
  float s = sin(angle);
  return mat3(
    1.0, 0.0, 0.0,
    0.0,   c,  -s,
    0.0,   s,   c
  );
}

mat3 we_mat3FromRotationY(float angle) {
  float c = cos(angle);
  float s = sin(angle);
  return mat3(
      c,0.0,  s,
    0.0,1.0,0.0,
     -s,0.0, c
  );
}

mat3 we_mat3FromRotationZ(float angle) {
  float c = cos(angle);
  float s = sin(angle);
  return mat3(
    c, -s, 0,
    s,  c, 0,
    0,  0, 1
  );
}

mat4 we_mat4FromRotation(mat3 rotation) {
    return mat4(
        vec4(rotation[0], 0.0),
        vec4(rotation[1], 0.0),
        vec4(rotation[2], 0.0),
        vec4(vec3(0.0), 1.0)
    );
}

mat4 we_mat4fromScale(vec3 scale) {
    return mat4(
        scale.x, 0.0,     0.0,     0.0,
        0.0,     scale.y, 0.0,     0.0,
        0.0,     0.0,     scale.z, 0.0,
        0.0,     0.0,     0.0,     1.0
    );
}

float we_angleBetweenVectors(vec2 v1, vec2 v2) {
    float dotProduct = dot(normalize(v1), normalize(v2));
    float angle = acos(clamp(dotProduct, -1.0, 1.0));
    return angle;
}
  `;

const WeDefineUseLegend = 'USE_LEGEND';
const WeDefineHasICON = 'HAS_ICON';
const WeDefineHasFilter = 'HAS_FILTER';
const WeDefineHasNormalMap = 'HAS_NORMAL_MAP';
const WeDefineHasContour = 'HAS_CONTOUR';
const WeDefineIsSampler3D = 'IS_SAMPLER3D';
const WeDefineCalculateNormal3D = 'WeDefineCalculateNormal3D'

const WeGeneratureFramgmentSource = function (defines,
  weUniformDeclar
  , samplerDeclare
  , fragmentShaderSource) {
  if (!fragmentShaderSource) {
    fragmentShaderSource = `
  in vec2 v_textureCoordinates;
  uniform vec4 u_localExtent;
  uniform sampler2D u_filterTexture;
  uniform bool u_hasFilterTexture;

  void main()
  {
    float minValue = Weget_minValue();
    float maxValue = Weget_maxValue();
    float filterMin = Weget_filterMin();
    float filterMax = Weget_filterMax();
    bool shouldFlipX = Weget_shouldFlipX() > 0.0;
    bool shouldFlipY = Weget_shouldFlipY() > 0.0;
    bool shouldClamp = Weget_clamp() > 0.0;
    vec4 worldExtent = Weget_extent();
    vec2 lonLat = WeGetImageryLonLat(v_textureCoordinates,u_localExtent);
    vec2 uv = WeGetImageryLocalUV(lonLat,worldExtent);
    if(shouldFlipX){
      uv.x = mod(uv.x + 0.5, 1.0);
    }
    if(shouldFlipY){
      uv.y = 1.0 - uv.y;
    }

    #ifdef ${WeDefineUseLegend}
    float value = WeDecodeValue(uv);

    float i = (value - minValue) / (maxValue - minValue);
    vec4 color = texture(u_TFTexture,vec2(i,0.0));
    if(shouldClamp){
      color.rgb = WeGetRampColor(color.rgb,u_TFTexture,value,minValue,maxValue);
    }

    color.a = WeIsNodata(value,minValue,filterMin,filterMax) ? 0.0 : color.a;
    #else
    vec4 color = texture(u_BaseTexture,uv);
    #endif

    #ifdef DEBUG_EXTENT
      color.a = 1.0;
    #endif

    #ifdef ${WeDefineHasNormalMap}
      WeNormalEffect(color, uv);
    #endif

    #ifdef ${WeDefineHasFilter}
    if(u_hasFilterTexture){
      vec2 st = vec2(v_textureCoordinates.x,1.0-v_textureCoordinates.y);
      color.a *= texture(u_filterTexture,st).a;
      // color = texture(u_filterTexture,st);
    }
    #endif


//     vec2 st = uv;
//     vec2 lineCount = u_textureResolution - vec2(1.0);
//     lineCount *= 0.125;
//     // lineCount *= 0.5;
//     vec2 lineThickness = vec2(1.0);

//     float scaledWidth = fract(lineCount.x * st.x);
//     scaledWidth = abs(scaledWidth - floor(scaledWidth + 0.5));
//     float scaledHeight = fract(lineCount.y * st.y);
//     scaledHeight = abs(scaledHeight - floor(scaledHeight + 0.5));

// #if (__VERSION__ == 300 || defined(GL_OES_standard_derivatives))
//     const float fuzz = 1.2;
//     vec2 thickness = (lineThickness * czm_pixelRatio) - 1.0;

//     // From "3D Engine Design for Virtual Globes" by Cozzi and Ring, Listing 4.13.
//     vec2 dx = abs(dFdx(st));
//     vec2 dy = abs(dFdy(st));
//     vec2 dF = vec2(max(dx.s, dy.s), max(dx.t, dy.t)) * lineCount;
//     float gridValue = min(
//         smoothstep(dF.s * thickness.s, dF.s * (fuzz + thickness.s), scaledWidth),
//         smoothstep(dF.t * thickness.t, dF.t * (fuzz + thickness.t), scaledHeight));
// #else
//     const float fuzz = 0.05;
//     vec2 range = 0.5 - (lineThickness * 0.05);
//     float gridValue = min(
//         1.0 - smoothstep(range.s, range.s + fuzz, scaledWidth),
//         1.0 - smoothstep(range.t, range.t + fuzz, scaledHeight));
// #endif
//     color.rgb = mix(color.rgb,color.rgb * 0.0,1.0 - gridValue);

    // {
    //   float spacing = 10.0;
    //   float distanceToContour = mod(value, spacing);
    //   float width = 2.0;
    //   #if (__VERSION__ == 300 || defined(GL_OES_standard_derivatives))
    //     float dxc = abs(dFdx(value));
    //     float dyc = abs(dFdy(value));
    //     float dF = max(dxc, dyc) * czm_pixelRatio * width;
    //     float contourAlpha = (distanceToContour < dF) ? 1.0 : 0.0;
    //   #else
    //     // If no derivatives available (IE 10?), use pixel ratio
    //     float contourAlpha = (distanceToContour < (czm_pixelRatio * width)) ? 1.0 : 0.0;
    //   #endif
    //   color.rgb = mix(color.rgb,vec3(1.0,1.0,1.0) * 0.0,contourAlpha);
    // }


    out_FragColor = czm_gammaCorrect(color);
  }`;
  }

  const shaderSource = new Cesium.ShaderSource({
    defines,
    sources:
      [
        samplerDeclare,
        WeGetImageryLocalUV,
        WeGetRampColor,
        weUniformDeclar,
        WeIsNodata,
        fragmentShaderSource],
  });

  return shaderSource;
};

const fixMaterialsSourface = (fragmentShaderSource, uniforms) => {
  let i = 0;
  let lastBaseTextureIndex = 0
  Object.keys(uniforms).forEach((element) => {
    const re = new RegExp(element + '\\b', 'g');
    if (element === 'u_LastBaseTexture') lastBaseTextureIndex = i

    fragmentShaderSource = fragmentShaderSource.replace(re, element + '_' + i++);
  });
  return fragmentShaderSource
}

export default {
  WeIsNodata,
  WeGetSubGridUV,
  WeGetImageryLocalUV,
  WeGetRampColor,
  WeDefineUseLegend,
  WeDefineHasContour,
  WeDefineHasFilter,
  WeDefineHasNormalMap,
  WeDefineHasICON,
  WeDefineIsSampler3D,
  WeGeneratureFramgmentSource,
  WeCartesian3FromRadians,
  WeDefineCalculateNormal3D,
  fixMaterialsSourface,
  WePhong,
}
